Skip to content

Replace range clet with linear-range backed by TG #5204 (D-025)#84

Merged
tig merged 24 commits intodevelopfrom
add-linear-range-clet
May 6, 2026
Merged

Replace range clet with linear-range backed by TG #5204 (D-025)#84
tig merged 24 commits intodevelopfrom
add-linear-range-clet

Conversation

@tig
Copy link
Copy Markdown
Member

@tig tig commented May 6, 2026

Target Terminal.Gui version: develop (post-#5204), shipped as the
LinearRange IValue<T> family:

  • LinearSelector<T> : LinearRangeViewBase<T, T>IValue<T>
  • LinearMultiSelector<T> : LinearRangeViewBase<T, IReadOnlyList<T>>IValue<IReadOnlyList<T>>
  • LinearRange<T> : LinearRangeViewBase<T, LinearRangeSpan<T>>IValue<LinearRangeSpan<T>>

Files

File Destination
LinearRangeClet.cs src/Clet/Clets/Input/LinearRangeClet.cs

Steps to land in clet

  1. Bump Terminal.Gui to the develop NuGet that contains #5204.
  2. Drop LinearRangeClet.cs under src/Clet/Clets/Input/.
  3. Register the clet in the registry. clet auto-discovers via
    reflection over IClet implementations in
    src/Clet/Registry/; if discovery is manual, add an entry alongside
    SelectClet and MultiSelectClet.
  4. Help text / clet list --json picks up the new clet automatically
    because Description, Aliases, and Options are reflected.
  5. Add a smoke test mirroring the shape of SelectClet's test:
    • non-interactive run with --initial, assert exit 0, JSON mode == "single",
      index matches.
    • --mode multi --initial "Pro,Team", assert indices set.
    • --mode range --initial "Pro..Team", assert kind == "closed",
      startIndex < endIndex.
  6. README — add a row under "Input clets":
    | linear-range | LinearRange (single, multi, or bounded range) |
    
    And a usage block:
    clet linear-range --mode single --options "Free,Pro,Team,Enterprise" --initial "Pro" --json
    clet linear-range --mode multi  --options "Mon,Tue,Wed,Thu,Fri,Sat,Sun" --initial "Mon,Tue,Wed,Thu,Fri" --json
    clet linear-range --mode range  --range-kind closed --options "8,9,10,11,12,13,14,15,16,17,18" --initial "9..17" --json

Distinct from range

RangeClet (the existing range command) takes a numeric low..high
with a --step and renders a custom numeric RangeView. That stays
unchanged. linear-range is the labelled-options companion that exposes
the full LinearRange family — different surface, different use case,
no overlap.

JSON contract (re-stated for reviewers)

// --mode single
{ "schemaVersion":1, "status":"ok", "mode":"single",
  "value":"Pro", "index":1 }

// --mode multi
{ "schemaVersion":1, "status":"ok", "mode":"multi",
  "values":["Mon","Tue"], "indices":[0,1] }

// --mode range, --range-kind closed
{ "schemaVersion":1, "status":"ok", "mode":"range", "kind":"closed",
  "start":"9", "end":"17", "startIndex":1, "endIndex":9 }

// --mode range, --range-kind left
{ "schemaVersion":1, "status":"ok", "mode":"range", "kind":"left",
  "end":"17", "endIndex":9 }

// --mode range, --range-kind right
{ "schemaVersion":1, "status":"ok", "mode":"range", "kind":"right",
  "start":"9", "startIndex":1 }

The schemaVersion / status envelope is added by the clet host; the
clet itself returns the inner JsonObject.

@tig tig changed the title Replace clet with backed by TG #5204 (D-025) Replace range clet with linear-range backed by TG #5204 (D-025) May 6, 2026
Copilot finished work on behalf of tig May 6, 2026 18:30
Deletes the hand-rolled RangeClet (NumericUpDown ×2 + ".." separator) and
its tests; adds LinearRangeClet backed by Terminal.Gui's `LinearRange<T>`
([gui-cs/Terminal.Gui#5204](gui-cs/Terminal.Gui#5204)).
The new clet uses a comma-separated option list and an optional
`--kind closed|left-bounded|right-bounded` (default `closed`).

Wire format changes from `{low, high}` to `{kind, start, end}`:

  closed:         {"kind":"closed","start":"<T>","end":"<T>"}
  left-bounded:   {"kind":"left-bounded","end":"<T>"}      (everything ≤ end)
  right-bounded:  {"kind":"right-bounded","start":"<T>"}   (everything ≥ start)

`<T>` is the option-label string. NoResult on empty selection.

D-011 ("range is integer-only at v0.3") is superseded — `linear-range` is
option-string typed, so the integer-only constraint no longer applies.

Why: the old RangeView's UX was poor (two independent spinners with no
visual sense of the relationship), it duplicated functionality TG was
about to ship in the right shape, and the new wire format honestly
encodes `LinearRangeSpan<T>` rather than clipping to closed-only.

Files
-----

Added:
  src/Clet/Clets/Input/LinearRangeClet.cs
  tests/Clet.UnitTests/LinearRangeCletTests.cs
  tests/Clet.IntegrationTests/LinearRangeCletIntegrationTests.cs

Deleted:
  src/Clet/Clets/Input/RangeClet.cs
  src/Clet/Clets/Input/RangeView.cs
  tests/Clet.UnitTests/RangeCletTests.cs
  tests/Clet.UnitTests/RangeViewTests.cs
  tests/Clet.IntegrationTests/RangeCletIntegrationTests.cs

Modified:
  src/Clet/Registry/BuiltInClets.cs   (RangeClet → LinearRangeClet)
  src/Clet/Clet.csproj                (TEMP ProjectReference to ../pr-5204…)
  tests/Clet.UnitTests/BuiltInCletsTests.cs (alias rename)
  specs/clet-spec.md                  (§4.3.2 row replaced)
  specs/decisions.md                  (D-025 added; D-011 superseded)
  README.md                           (v1.0 input list)

Dependency
----------

This PR depends on TG #5204 merging and a TG develop NuGet shipping
with the new `LinearRange<T> : IValue<LinearRangeSpan<T>>` API. Until
then, `Clet.csproj` uses a `<ProjectReference>` to a local TG worktree
at `../pr-5204-linear-range/` (clearly marked TEMP — same pattern used
for PR #5153). Hold this PR in draft until TG #5204 merges, then revert
the csproj to `<PackageReference Include="Terminal.Gui" Version="$(TerminalGuiVersion)" />`
and bump `<TerminalGuiVersion>` to a develop build that includes the
refactor.

Verified locally against the worktree
-------------------------------------

  Unit:        186 / 186
  Integration: 44 (2 pre-existing skips)
  Smoke:       8 (1 pre-existing skip)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tig tig force-pushed the add-linear-range-clet branch from ab10f72 to c3774ce Compare May 6, 2026 18:44
@tig
Copy link
Copy Markdown
Member Author

tig commented May 6, 2026

The fix has been deployed in TG. gui-cs/Terminal.Gui#5204

This can be completed now.

@tig tig unassigned Copilot May 6, 2026
Copilot stopped work on behalf of tig due to an error May 6, 2026 21:18
@gui-cs gui-cs deleted a comment from Copilot AI May 6, 2026
tig and others added 18 commits May 6, 2026 16:01
…tion

- Merge develop (picks up D-029..D-031, TerminalEscapeSanitizer,
  JSON serialization fixes, release workflow improvements)
- Revert csproj from local TG project reference to NuGet package
- Fix unit tests: option names (kind→mode, add range-kind),
  remove nonexistent validation error test
- Fix integration tests: use mode/range-kind, not kind
- Fix README table: range→linear-range with correct options
- Fix spec §4.3.2: wire format now documents all three modes
- Fix decisions: renumber to D-032 (D-029..D-031 taken by develop),
  document actual --mode/--range-kind options
- Add missing System.Text using for Rune

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- linear-range → also responds to range
- attribute-picker → also responds to attribute

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
BuildAliasHelpMarkdown now appends an embedded Help/<alias>.md if
one exists, so any clet can have rich examples by adding a file.

Add Help/linear-range.md with examples for single, multi, and
range modes, range kinds, and layout options.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Multi example uses --options instead of positional args (matches
  how --mode is passed)
- Add numeric range example: seq 0 0.1 10.0 piped as positional
  args, with PowerShell equivalent
- Use short alias (range) consistently in examples

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- clet help --cat [alias] renders help to stdout as ANSI text
- clet help range --cat works too (flag order doesn't matter)
- clet range help, clet range --help, clet range -h all dispatch
  to the help viewer for that alias
- clet range help --cat combines both: renders help to stdout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Help pages are now interlinked:
- The overview's Available Clets table links each primary alias
  to its help page via clet:help:<alias> links
- Each clet's help page has a "Back to overview" link at the bottom

Uses a LinkResolver callback on CletRunOptions. DispatchHelp sets
it to a closure that builds help content on demand. MarkdownClet's
LinkClicked handler checks the resolver before falling through to
the default SurfaceOnly behavior.

Clicking a clet name in `clet help` navigates to that clet's help
in-place. Clicking "Back to overview" returns. No process restart,
no temp files — content is swapped in the same viewer session.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backticks inside link text ([`select`](url)) confused the markdown
parser — rendered as literal backtick-quoted text instead of a
clickable link. Use plain text in link labels instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Links inside table cells aren't clickable (gui-cs/Terminal.Gui#5227).
Revert table aliases to plain backtick text and add a "Click for
details:" line after the table with working links to each clet's
help page.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Every clet now has a Help/<alias>.md with usage examples and
sample JSON output. Shown automatically by `clet help <alias>`
or `clet <alias> --help`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
clet help (and clet help help) now shows examples for navigating
help: --cat, per-clet help, and the equivalent alias-first forms.
Help examples are appended to the overview page.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add [Help on help](clet:help:help) link in the overview after the
Usage summary. Clicking it navigates to a dedicated help page with
examples. No longer appended to the bottom of every overview render.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace ~80 lines of help special-casing in CommandLineRoot with
a proper HelpClet registered in BuiltInClets.

HelpClet:
- Registered as a viewer clet with alias "help"
- AcceptsPositionalArgs (the alias to show help for)
- Builds help content dynamically from the registry
- Handles inter-page navigation via clet:help: links internally
- Handles --cat internally (renders to stdout, returns Ok)
- Has its own Markdown viewer with status bar

CommandLineRoot changes:
- Removed case "help:" block (falls through to DispatchAlias)
- Removed DispatchHelp and BuildHelpContent methods
- clet <alias> help rewrites args as clet help <alias> and re-dispatches
- Removed LinkResolver from CletRunOptions (no longer needed)

AliasDispatcher:
- --cat path now falls through to RunAsync when no static content is
  resolved, so clets that build content dynamically can handle --cat

Closes #105

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
clet:help:help was matching the null-or-help case and returning
the overview. Now alias "help" has its own branch that builds the
help-on-help page with examples from Help/help.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Stack overflow on `clet help help`: the alias redirect in
   DispatchAlias now skips when alias is already "help", preventing
   infinite ["help","help"] → ["help","help"] recursion.

2. Unknown alias prints error instead of opening TUI: `clet help xyz`
   now returns an error with "Unknown alias" message and exits 2.

3. --cat output garbled by OSC 8 links: clet:help:* links are
   TUI-only. Strip them (convert [text](clet:help:x) → text) before
   rendering in --cat mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The TerminalEscapeSanitizer was stripping ALL OSC sequences
including OSC 8 (hyperlinks), which are legitimate renderer output
for terminals like Kitty and Ghostty. Now OSC 8 sequences are
passed through while other OSC sequences (window title, clipboard,
etc.) are still stripped.

Reverts the regex link-stripping in HelpClet — with OSC 8
preserved, the links render as clickable hyperlinks in --cat mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Help content is authored by us — all links are safe. Clicking a
non-clet: URL in help now opens it via Link.OpenUrl (default
browser) in addition to showing it in the status bar.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Clicking an http:// or https:// link in clet md now opens it in
the default browser via Link.OpenUrl. These are safe — worst case
the user sees a webpage. The URL is still shown in the status bar.

Local .md file navigation (within CWD sandbox) will come when
PR #96 merges.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Merge develop which includes PR #96 (FileAccessPolicy, local .md
link navigation, --allow-file directory support).

MarkdownClet LinkClicked now:
1. Navigates local .md files within the sandbox (from #96)
2. Opens http/https links in the default browser (safe)
3. Shows the URL in the status bar as a clickable link

Resolve decisions.md conflict: linear-range is D-032, file-access
confinement is D-033.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tig tig marked this pull request as ready for review May 6, 2026 23:26
tig and others added 3 commits May 6, 2026 17:29
1. HelpAlias_UnknownAlias: match "Unknown alias" (uppercase U) —
   HelpClet returns this via OutputFormatter, not the old direct
   stderr.WriteLine.

2. MdCat_NoContent: expect IoError (74) not UsageError (2) —
   AliasDispatcher --cat now falls through to MarkdownClet.RunAsync
   which returns "io" error for no file specified.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. UnknownAlias test: AliasDispatcher writes "unknown alias"
   (lowercase) — revert the incorrect uppercase change.

2. MdCat_NoContent: stdin is redirected in test runners, so
   MarkdownClet reads empty stdin → "No input received from stdin"
   rather than "No file specified". Accept either message.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tig tig merged commit 7496438 into develop May 6, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants